<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>轮播图</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        a {
            text-decoration: none;
        }

        /* 轮播图的容器 */
        .container {
            width: 50em;
            height: 22em;
            margin: 1em auto;
            /* 转为定位元素/定位父级 */
            position: relative;
        }

        /* 图片组 */
        .container>.imgs img {
            width: 100%;
            height: 100%;

            /* 默认全部隐藏 */
            display: none;

            /* 将所有的图片进行绝对定位，确保每一次只看到一张,所有图片共享这个容器 */
            position: absolute;
            left: 0;
            top: 0;
        }

        /* 设置默认显示的图片(第一张) */
        .container>.imgs img.active {
            display: block;
        }

        /* 按钮组(独立按钮) */
        .container>.btns {
            position: absolute;
            left: 0;
            right: 0;
            bottom: 0;
            /* 水平居中 */
            text-align: center;
        }

        .container>.btns a {
            /* 转成行内块元素: 即能水平排列,双支持宽度设置 */
            display: inline-block;
            padding: 0.5em;
            margin: 0 0.2em;
            background-color: #fff;
            border-radius: 50%;
        }

        .container>.btns a.active {
            background-color: #000;
        }

        /* 翻页按钮 */
        .container .skip a {
            position: absolute;
            width: 2.5rem;
            height: 5rem;
            line-height: 5rem;
            text-align: center;
            opacity: 0.3;
            top: 8rem;
            font-weight: lighter;

            font-size: 2rem;
            background-color: #ccc;
        }

        .container .skip .prev {
            left: 0;
        }

        .container .skip .next {
            right: 0;
        }

        .container .skip *:hover {
            opacity: 0.6;
            color: #666;
        }
    </style>

    <script>
        document.addEventListener('DOMContentLoaded', function () {
            class Slider {
                // 构造方法
                constructor(slider = { container: '.container', slide: '.slide' }) {
                    this.slider = slider;
                    // 外层容器
                    this.container = this.slider['container'];
                    // 轮播的项目
                    this.slide = this.slider['slide'];
                    // 按钮组
                    this.btns = 'undefined' !== typeof this.slider['btns'] ? this.slider['btns'] : '.btns';
                    // 前后翻页
                    this.skip = 'undefined' !== typeof this.slider['skip'] ? this.slider['skip'] : '.skip';
                    // 事件类型
                    this.event = 'undefined' !== typeof this.slider['event'] ? this.slider['event'] : 'click';
                    // 载入时自动播放
                    this.autoplay = 'undefined' !== typeof this.slider['autoplay'] ? this.slider['autoplay'] : true;
                    // 播放延时
                    this.time = 'undefined' !== typeof this.slider['time'] ? this.slider['time'] : 2000;
                    // 初始化数据
                    this.imgs = document.querySelectorAll(this.slide); //项目组
                    this.btnGroup = document.querySelector(this.btns); //按钮组
                    this.skip = document.querySelector(this.skip); //翻页按钮
                    this.init(); //初始执行
                }

                // 执行函数
                init() {
                    // 为项目组每个项目添加自定义属性索引
                    this.imgs.forEach((img, i) => {
                        img.dataset.index = i;
                        if (i === 0) img.classList.add('active');
                    });

                    // 根据项目数量向按钮组btnGroup添加项目数量个按钮
                    this.autoCreateBtn();

                    // 为每个小按钮添加点击事件
                    [...this.btnGroup.children].forEach(btn => btn.addEventListener(this.event, ev => this.setActiveEle(ev.target.dataset.index)));

                    // 为prev前翻页元素添加事件监听器
                    this.skip.firstElementChild.classList.add('prev');
                    this.skip.firstElementChild.addEventListener(this.event, ev => this.skipImg(ev));
                    // 为next后翻页元素添加事件监听器
                    this.skip.lastElementChild.classList.add('next');
                    this.skip.lastElementChild.addEventListener(this.event, ev => this.skipImg(ev));

                    // 定义一个点击事件
                    let clickEvent = new Event(this.event);
                    // 页面载入时，为next后翻页元素，定时器间隔派发点击事件
                    let timer = null;
                    // 如果载入时，允许进入自动播放
                    this.autoplay && (timer = setInterval(() => this.skip.lastElementChild.dispatchEvent(clickEvent), this.time));

                    // 事件代理，鼠标移入/出轮播区时，事件派发执行
                    const slider = document.querySelector('.container');
                    // 鼠标移出轮播区域自动轮播
                    // 为next后翻页元素，定时器间隔派发点击事件
                    slider.addEventListener('mouseout', () => {
                        timer = setInterval(() => this.skip.lastElementChild.dispatchEvent(clickEvent), this.time);
                    });
                    // 鼠标移入轮播区域停止轮播
                    // 清除定时器
                    slider.addEventListener('mouseover', () => {
                        clearInterval(timer);
                    });
                }

                // 创建按钮组
                autoCreateBtn() {
                    this.imgs.forEach((img, i) => {
                        // 创建并设置 a 标签属性
                        let a = document.createElement('a');
                        a.href = "#";
                        a.dataset.index = i;
                        // 图片处以激活时，对应的按钮也处于激活状态
                        if (img.classList.contains('active')) {
                            a.classList.add('active');
                        }
                        // 追加到父级元素
                        this.btnGroup.appendChild(a);
                    });
                }

                // 获取激活的项目
                getActiveEle(elem) {
                    // 转为数组过滤出包含 active 类的元素出队
                    return [...elem].filter(ele => ele.classList.contains('active')).shift();
                }

                // 设置激活的元素
                setActiveEle(index) {
                    //第 index 图片和按钮激活时
                    [this.imgs, [...this.btnGroup.children]].forEach(elem => elem.forEach(ele => {
                        // 取消当前元素激活
                        if (ele.classList.contains('active')) ele.classList.remove('active');
                        // 激活第 index 的图片和按钮
                        if (index === ele.dataset.index) ele.classList.add('active');
                    }
                    ));
                }

                // 前后prev|next翻页点击事件的回调函数
                skipImg(ev) {
                    // 计算前后翻页点击时，图片组的图片或按钮组的按钮，处以激活状态的位置索引
                    let posIndex = parseInt(this.getActiveEle(this.imgs).dataset.index);
                    // 向前翻页时（包含一个css类prev）
                    if (ev.target.classList.contains('prev')) {
                        if (0 === posIndex) {
                            // 原激活索引是 0，表示前面没有图片，则赋值最后一个图片的位置索引
                            posIndex = this.imgs.length - 1;
                        } else {
                            // 否则，表示前面还有图片，赋值前一个图片的位置索引，供前翻页激活
                            posIndex = posIndex - 1;
                        }
                    }
                    // 向后翻页时（包含一个css类next）
                    if (ev.target.classList.contains('next')) {
                        if (posIndex === this.imgs.length - 1) {
                            // 原激活索引是最后一个图片，表示后面没有没有图片了，赋值第一个图片的索引
                            posIndex = 0;
                        } else {
                            // 否则，表示后面还有图片，赋值后一个图片的位置索引，供后翻页激活
                            posIndex = posIndex + 1;
                        }
                    }
                    // 激活第 posIndex 元素(图片|按钮)
                    this.setActiveEle(posIndex.toString());
                }
            }

            // 调用
            let slider = new Slider({
                container: '.container', // 外层容器
                slide: '.container > .imgs img', // 轮播的项目
                btns: '.container > .btns', // 生成按钮组的容器css
                skip: '.container > .skip', // 前后翻页的css
                event: 'click', // 事件类型
                autoplay: true, // 载入时是否允许自动播放
                time: 2000 // 延时间隔时间
            });
        });
    </script>
</head>

<body>
    <div class="container">
        <!-- 1. 图片组，使用封装，去除自定义属性索引 -->
        <nav class="imgs">
            <a href="#"><img src="./images/focus.jpg" alt="" /></a>
            <a href="#"><img src="./images/focus1.jpg" alt="" /></a>
            <a href="#"><img src="./images/focus2.jpg" alt="" /></a>
            <a href="#"><img src="./images/focus3.jpg" alt="" /></a>
        </nav>
        <!-- 2. 图片小按钮 -->
        <nav class="btns"></nav>
        <!-- 3. 翻页 -->
        <nav class="skip">
            <a href="#">&lt;</a>
            <a href="#">&gt;</a>
        </nav>
    </div>
</body>

</html>